#include "stdafx.h"
#include "tabfile.h"

#include "ifile.h"
#include "string_func.h"

CTabFile::CTabFile()
{
	m_pData = NULL;
	m_nSize = 0;
	m_nRowCount = 0;
	m_nColCount = 0;
	m_pOffsetTable = NULL;
}

CTabFile::~CTabFile()
{
	UnInit();
}

bool CTabFile::Init(const char* pcszFileName)
{
	IFile* pFile = NULL;
	unsigned int uBOM = 0;

	LOG_PROCESS_ERROR(pcszFileName);

	// read file
	pFile = g_OpenFile(pcszFileName, "rb");
	LOG_PROCESS_ERROR(pFile);

	m_nSize = pFile->Size();
	LOG_PROCESS_ERROR(m_nSize);

	int nReadSize = pFile->Read(&uBOM, 1, sizeof(uBOM));

	if ((uBOM & 0xFFFF) == UTF16_BOM)
	{
		m_nSize -= 2;
		pFile->Seek(2, SEEK_SET);
	}
	else if ((uBOM & 0xFFFFFF) == UTF8_BOM)
	{
		m_nSize -= 3;
		pFile->Seek(3, SEEK_SET);
	}
	else	// utf8 default
	{
		pFile->Seek(0, SEEK_SET);
	}

	LOG_PROCESS_ERROR(m_pData == NULL);
	m_pData = new char[m_nSize];
	LOG_PROCESS_ERROR(m_pData);

	nReadSize = pFile->Read(m_pData, 1, m_nSize);
	LOG_PROCESS_ERROR(nReadSize == m_nSize);

	g_CloseFile(pFile);
	pFile = NULL;

	// convert
	if ((uBOM & 0xFFFF) == UTF16_BOM)
	{
		wchar_t* pszData = (wchar_t*)m_pData;
		
		m_pData = new char[m_nSize * 3 / 2];
		LOG_PROCESS_ERROR(m_pData);

		m_nSize = utf16_2_utf8(pszData, m_nSize / 2, m_pData, m_nSize * 3 / 2);
		LOG_PROCESS_ERROR(m_nSize > 0);

		SAFE_DELETE_ARRAY(pszData);
	}

	// first scan
	int nIndex = 0;
	m_nColCount = 1;
	m_nRowCount = 0;
	bool bInQuote = false;
	int nTabCount = 0;
	while (nIndex < m_nSize)
	{
		switch (m_pData[nIndex])
		{
		case '\t':
			if (m_nRowCount == 0 && !bInQuote)
				m_nColCount++;
			nTabCount++;
			break;
		case '\r':
			if (!bInQuote)
			{
				m_nRowCount++;
			}
			break;
		case '\"':
			if (bInQuote)
			{
				if (m_pData[nIndex + 1] != '\"')
					bInQuote =  false;
				else
					nIndex++;
			}
			else
				bInQuote = true;
			break;
		}

		nIndex++;
	}

	LOG_PROCESS_ERROR(m_pOffsetTable == NULL);
	m_pOffsetTable = new int[m_nColCount * m_nRowCount];
	LOG_PROCESS_ERROR(m_pOffsetTable);

	// second scan
	nIndex = 0;
	int nStringIndex = 0;
	int nOffsetIndex = 0;
	bInQuote = false;
	while (nIndex < m_nSize)
	{
		switch (m_pData[nIndex])
		{
		case '\t':
		case '\r':
			if (!bInQuote)
			{
				m_pData[nIndex] = 0;

				while (m_pData[nStringIndex] == 0 ||
					m_pData[nStringIndex] == '\r' ||
					m_pData[nStringIndex] == '\n')
					nStringIndex++;

				LOG_PROCESS_ERROR(nOffsetIndex < m_nColCount * m_nRowCount);
				m_pOffsetTable[nOffsetIndex] = nStringIndex;
				nOffsetIndex++;

				nStringIndex = nIndex + 1;
			}
			break;
		case '\"':
			if (bInQuote)
			{
				if (m_pData[nIndex + 1] == '\"')
				{
					int nTempIndex = nIndex;
					while (m_pData[nTempIndex] != 0 && m_pData[nTempIndex+1] != '\r' && m_pData[nTempIndex+1] != '\t')
					{
						m_pData[nTempIndex] = m_pData[nTempIndex + 1];
						nTempIndex++;
					}
					m_pData[nTempIndex] = 0;
					nIndex++;
				}
				else
				{
					bInQuote =  false;
					m_pData[nIndex] = 0;
				}
			}
			else
			{
				bInQuote = true;
				m_pData[nIndex] = 0;
			}

			break;
		}

		nIndex++;
	}

	// create name map
	for (int nIndex = 0; nIndex < m_nColCount; nIndex++)
	{
		std::pair<MAP_NAME_2_COL::iterator, bool> InsRet;

		const char* pcszValue = GetData(nIndex, 0);
		LOG_PROCESS_ERROR(pcszValue);

		InsRet = m_mapName2Col.insert(std::make_pair(pcszValue, nIndex));
		LOG_PROCESS_ERROR(InsRet.second);
	}

	return true;
Exit0:
	if (pFile)
		g_CloseFile(pFile);

	UnInit();

	return false;
}

bool CTabFile::UnInit(void)
{
	SAFE_DELETE_ARRAY(m_pData);
	SAFE_DELETE_ARRAY(m_pOffsetTable);

	return true;
}

int CTabFile::GetRowCount(void)
{
	return m_nRowCount;
}

int CTabFile::GetColCount(void)
{
	return m_nColCount;
}

const char* CTabFile::GetData(int nCol, int nRow)
{
	LOG_PROCESS_ERROR(nCol >= 0 && nCol < m_nColCount);
	LOG_PROCESS_ERROR(nRow >= 0 && nRow < m_nRowCount);

	return m_pData + m_pOffsetTable[nRow * m_nColCount + nCol];
Exit0:
	return NULL;
}

const char* CTabFile::GetData(const char* pcszColName, int nRow)
{
	MAP_NAME_2_COL::iterator it;

	it = m_mapName2Col.find(pcszColName);
	LOG_PROCESS_ERROR(it != m_mapName2Col.end());

	return GetData(it->second, nRow);
Exit0:
	return NULL;
}
